#include <Common/likePatternToRegexp.h>

#include <Common/Exception.h>

namespace DB
{

namespace ErrorCodes
{
    extern const int CANNOT_PARSE_ESCAPE_SEQUENCE;
}

String likePatternToRegexpWithEscape(std::string_view pattern, char escape_char)
{
    String res;
    res.reserve(pattern.size() * 2);

    const char * pos = pattern.data();
    const char * const end = pattern.begin() + pattern.size();

    if (pos < end && *pos == '%')
        /// Eat leading %
        while (++pos < end)
        {
            if (*pos != '%')
                break;
        }
    else
        res = "^";

    while (pos < end)
    {
        if (*pos == escape_char) {
            if (pos + 1 == end)
                throw Exception(ErrorCodes::CANNOT_PARSE_ESCAPE_SEQUENCE, "Invalid escape sequence at the end of LIKE pattern '{}'", pattern);

            if (pos[1] == escape_char) {
                res += '\\'; /// backslash has a special meaning in re2 --> quote it
                res += escape_char;
                ++pos;
            } else if (pos[1] == '%' || pos[1] == '_') {
                /// Interpret quoted LIKE metacharacters %, _ and \ as literals:
                res += pos[1];
                ++pos;
            } else {
                /// Unknown escape sequence treated literally: as backslash (which must be quoted in re2) + the following character
                res += '\\';
                res += escape_char;
            }
        } else {
            switch (*pos)
            {
                /// Quote characters which have a special meaning in re2
                case '^':
                case '$':
                case '.':
                case '[':
                case '|':
                case '(':
                case ')':
                case '?':
                case '*':
                case '+':
                case '{':
                    res += '\\';
                    res += *pos;
                    break;
                case '%':
                    if (pos + 1 != end)
                        res += ".*";
                    else
                        return res;
                    break;
                case '_':
                    res += ".";
                    break;
                default:
                        res += *pos;
                    break;
            }
        }
        ++pos;
    }

    res += '$';
    return res;
}

String likePatternToRegexp(std::string_view pattern)
{
    String res;
    res.reserve(pattern.size() * 2);

    const char * pos = pattern.data();
    const char * const end = pattern.begin() + pattern.size();

    if (pos < end && *pos == '%')
        /// Eat leading %
        while (++pos < end)
        {
            if (*pos != '%')
                break;
        }
    else
        res = "^";

    while (pos < end)
    {
        switch (*pos)
        {
            /// Quote characters which have a special meaning in re2
            case '^':
            case '$':
            case '.':
            case '[':
            case '|':
            case '(':
            case ')':
            case '?':
            case '*':
            case '+':
            case '{':
                res += '\\';
                res += *pos;
                break;
            case '%':
                if (pos + 1 != end)
                    res += ".*";
                else
                    return res;
                break;
            case '_':
                res += ".";
                break;
            case '\\':
                if (pos + 1 == end)
                    throw Exception(ErrorCodes::CANNOT_PARSE_ESCAPE_SEQUENCE, "Invalid escape sequence at the end of LIKE pattern '{}'", pattern);
                switch (pos[1])
                {
                    /// Interpret quoted LIKE metacharacters %, _ and \ as literals:
                    case '%':
                    case '_':
                        res += pos[1];
                        ++pos;
                        break;
                    case '\\':
                        res += "\\\\"; /// backslash has a special meaning in re2 --> quote it
                        ++pos;
                        break;
                    /// Unknown escape sequence treated literally: as backslash (which must be quoted in re2) + the following character
                    default:
                        res += "\\\\";
                        break;
                }
                break;
            default:
                res += *pos;
                break;
        }
        ++pos;
    }

    res += '$';
    return res;
}

}
